package com.k.kc.util;

import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;

/**
 * @author KK
 */
public class FileUtils {
    public static final long ONE_KB = 1024L;
    public static final long ONE_MB = 1048576L;
    private final static String OS_WINDOWS = "WINDOWS";
    private final static String OS_LINUX = "LINUX";

    public static String formatDir(String dir) {
        String fDir;
        String osName = System.getenv("OS");
        if(osName == null){
            fDir = dir;
        }else if(osName.toUpperCase().contains(OS_WINDOWS)){
            fDir = dir.replace("/", "\\");
        }else if(osName.toUpperCase().contains(OS_LINUX)){
            fDir = dir.replace("\\", "/");
        }else{
            fDir = dir.replace("/", "\\");
        }
        return File.separator.equals(fDir.substring(fDir.length() - 1)) ? fDir : fDir + File.separator;
    }

    /**
     *
     * @param orignialFilename file.getOriginalFilename() contains 空格 中文 特殊字符
     * @return filename
     * @throws UnsupportedEncodingException e
     */
    public static String formatFilename(String orignialFilename) throws UnsupportedEncodingException {
        String fileName = orignialFilename==null?IDGenerator.id():orignialFilename.replaceAll("%(?![0-9a-fA-F]{2})","%25");
        return URLDecoder.decode(fileName,"utf-8");
    }

    public static void createDir(String dir) throws IOException {
        File f = new File(dir);
        if(!f.exists()){
            boolean isMkdirs = f.mkdirs();
            if(!isMkdirs){
                throw new IOException("dir not found create dir error:"+dir);
            }
        }
    }

    public static String getFilePath(String dir,String name){
        String filePath;
        if(File.separator.equals(dir.substring(dir.length()-1))){
            filePath = dir + name;
        }else{
            filePath = dir +File.separator + name;
        }
        return filePath;
    }

    public boolean checkFile(File file){
        return file != null  && file.length()>0 && file.length() <= ONE_MB * 10;
    }

    public static void save(String srcDir, String srcName, String destDir, String destName) throws IOException {
        save(srcDir + srcName, destDir + destName);
    }

    public static void save(String srcPath, String destPath) throws IOException {
        try (FileInputStream fis = new FileInputStream(srcPath);
             FileOutputStream fos = new FileOutputStream(destPath)) {
            save(fis, fos);
        } catch (IOException e) {
            throw new IOException(e);
        }
    }

    public static void save(File srcFile, File destFile) throws IOException {
        try (FileInputStream fis = new FileInputStream(srcFile);
                FileOutputStream fos = new FileOutputStream(destFile)) {
            save(fis, fos);
        } catch (IOException e) {
            throw new IOException(e);
        }
    }

    public static void save(InputStream is, OutputStream os) throws IOException {
        int len;
        while ((len = is.read()) != -1) {
            os.write(len);
        }
    }

    public static void save(InputStream is, OutputStream os,byte[] buf) throws IOException {
        int len;
        while ((len = is.read(buf)) != -1) {
            os.write(buf, 0, len);
        }
    }

    private static long copy(InputStream is, OutputStream os,int bufferSize) throws IOException {
        long nRead = 0L;
        byte[] buf = new byte[bufferSize>0?bufferSize:8192];
        int n;
        while ((n = is.read(buf)) > 0) {
            os.write(buf, 0, n);
            nRead += n;
        }
        return nRead;
    }
    private static long copy(InputStream is, OutputStream os) throws IOException {
        int defaultBufferSize = 8192;
        return copy(is,os,defaultBufferSize);
    }


    /**
     * copy jar nei file to sysdir
     * copyJarfileToSys("/org/sqlite/native/Windows/x86_64","sqlitejdbc.dll","c:/test/dll","ljw.dll");
     * @param inFileDir /com/a
     * @param inFileName  jdbc.dll
     * @param outFileDir c:/test/dll
     * @param outFileName jdbcout.dll
     */
    public static void copyJarfileToSys(String inFileDir ,String inFileName,String outFileDir,String outFileName){
        try {
            String inFilePath = inFileDir+"/"+inFileName;
            InputStream reader = FileUtils.class.getResourceAsStream(inFilePath);

            File outFile = new File(outFileDir, outFileName);
            FileOutputStream writer = new FileOutputStream(outFile);
            try {
                byte[] buffer = new byte[8192];
                int bytesRead = 0;
                while ((bytesRead = reader.read(buffer)) != -1) {
                    writer.write(buffer, 0, bytesRead);
                }
            } finally {
                // Delete the extracted lib file on JVM exit.
                //outFile.deleteOnExit();
                //outFileLck.deleteOnExit();
                if (writer != null) {
                    writer.close();
                }
                if (reader != null) {
                    reader.close();
                }
            }
            outFile.setReadable(true);
            outFile.setWritable(true, true);
            outFile.setExecutable(true);
            // check file
            {
                InputStream nativeIn = FileUtils.class.getResourceAsStream(inFilePath);
                InputStream extractedLibIn = new FileInputStream(outFile);
                try {
                    if(!contentsEquals(nativeIn, extractedLibIn)) {
                        throw new RuntimeException(String.format("Failed to write a native library file at %s", outFile));
                    }
                }
                finally {
                    if(nativeIn != null) {
                        nativeIn.close();
                    }
                    if(extractedLibIn != null) {
                        extractedLibIn.close();
                    }
                }
            }
        } catch (IOException e) {
            System.err.println(e.getMessage());
        }
    }
    public void backupFile(String filePath,String backupFilePath){

    }
    private static boolean contentsEquals(InputStream in1, InputStream in2) throws IOException {
        if(!(in1 instanceof BufferedInputStream)) {
            in1 = new BufferedInputStream(in1);
        }
        if(!(in2 instanceof BufferedInputStream)) {
            in2 = new BufferedInputStream(in2);
        }
        int ch = in1.read();
        while(ch != -1) {
            int ch2 = in2.read();
            if(ch != ch2) {
                return false;
            }
            ch = in1.read();
        }
        int ch2 = in2.read();
        return ch2 == -1;
    }


    /**
     * <p>
     * in: {"usr","cdss","upload"}
     * out:linux: /usr/cdss/upload/
     *     windows:c:|usr|cdss|upload|
     *</p>
     * @param fileDirArr array
     * @return dir
     */
    public static String formatFileDir(String[] fileDirArr){
        String gang = File.separator;
        String dirJoin = StringUtils.join(fileDirArr,gang);
        String os = System.getProperty("os.name");
        String root = gang;
        if (os.toLowerCase().startsWith("win")) {
            root = "c:" + gang;
        }
        return root + dirJoin + gang;
    }

    private String formatFileName(String fileName) throws UnsupportedEncodingException {
        return formatFilename(fileName);
    }

    /**
     *  in: {"usr","cdss","upload"} , a.txt
     *  linux out: /usr/cdss/upload/a.txt
     *  windows out: c:|usr|cdss|upload|a.txt
     * @param fileDirArr array
     * @param fileName filename
     * @return filePathname
     */
    public String formatFilePathname(String[] fileDirArr,String fileName) throws UnsupportedEncodingException {
        return formatFileDir(fileDirArr) + formatFileName(fileName);
    }

    /**
     * file to string
     * @param input InputStream
     * @param encoding encoding
     * @return result
     * @throws IOException e
     */
    public static List<String> readLines(InputStream input, Charset encoding) throws IOException {
        List<String> list = new ArrayList<>();
        try(InputStreamReader sReader = new InputStreamReader(input, encoding.name());
            BufferedReader bReader = new BufferedReader(sReader)){
            for(String line = bReader.readLine(); line != null; line = bReader.readLine()) {
                list.add(line);
            }
        }
        return list;
    }

    /**
     * file to string
     * @param filePathname filePathname
     * @param encoding encoding
     * @return result
     * @throws IOException e
     */
    public static List<String> readLines(String filePathname,Charset encoding) throws IOException {
        List<String> result = new ArrayList<>();
        File file = new File(filePathname);
        try (FileInputStream fis = new FileInputStream(file);
             InputStreamReader isr = new InputStreamReader(fis, encoding.name());
             BufferedReader bReader = new BufferedReader(isr)){
            for(String line = bReader.readLine(); line != null; line = bReader.readLine()) {
                result.add(line);
            }
        }
        return result;
    }

    public static BufferedReader toBufferedReader(Reader reader) {
        return reader instanceof BufferedReader?(BufferedReader)reader:new BufferedReader(reader);
    }

    /**
     * read file content
     * <code>
     *     BufferedReader brElf = new BufferedReader(new InputStreamReader(new FileInputStream(new File("/usr/test/a.txt"))));
     * </code>
     * @param reader reader
     * @param delimiter delimiter
     * @return List string
     * @throws IOException e
     */
    public static List<String> readBySplit(Reader reader, String delimiter) throws IOException {
        List<String> result = new ArrayList<>();
        try(BufferedReader br = toBufferedReader(reader)){
            StringBuilder sb = new StringBuilder();
            String temp = br.readLine();
            System.out.println(temp);
            while (temp != null) {
                if (temp.startsWith("#")
                        || temp.startsWith("--")
                        || temp.startsWith("/*")
                        || temp.startsWith("//") || temp.trim().length()==0) {
                    temp = br.readLine();
                    continue;
                }
                sb.append(temp).append(System.lineSeparator());
                if (temp.endsWith(delimiter)) {
                    result.add(sb.toString());
                    sb.delete(0, sb.length());
                    sb.trimToSize();
                }
                temp = br.readLine();
            }
        }catch (Exception e){
            throw new IOException(e);
        }
        return result;
    }

    public static List<String> readBySplit(InputStream is, Charset encoding, String delimiter) throws IOException {
        return readBySplit(new InputStreamReader(is, encoding.name()), delimiter);
    }

    public static List<String> readBySplit(File file, Charset encoding, String delimiter) throws IOException {
        return readBySplit(new FileInputStream(file), encoding, delimiter);
    }

    public static List<String> readBySplit(String filePathname, Charset encoding, String delimiter) throws IOException {
        return readBySplit(new File(filePathname),encoding,delimiter);
    }

    public static byte[] readFileToByteArray(String filePathname) throws IOException {
        byte[] b;
        try(
                FileInputStream fis = new FileInputStream(new File(filePathname));
                ByteArrayOutputStream bos = new ByteArrayOutputStream()
        ){
            int i;
            while ((i = fis.read()) != -1){
                bos.write(i);
            }
            fis.close();
            b = bos.toByteArray();
        }
        return b;
    }


    // ***********************************************************************
    //                       test methods
    // ***********************************************************************

    public static void main(String[] args) {
        testReadLines("C:\\test\\文本编码\\dd.txt", StandardCharsets.UTF_8);
        testReadBySplit("C:\\test\\upd\\init_Database.sql",";;;", StandardCharsets.UTF_8);
    }

    private static void printList(List<String> list){
        for(int i=0;i<list.size();i++){
            System.out.println(i+" | "+ list.get(i));
        }
    }

    private static void testReadLines(String path,Charset charset){
        try {
            List<String> list = readLines(path,charset);
            printList(list);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void testReadBySplit(String filePathname,String delimiter,Charset charset){
        try {
            List<String> list = FileUtils.readBySplit(filePathname,charset,delimiter);
            printList(list);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void testUpload(){
        String srcDir = "C:/test/";
        String srcName = "Log.rar";
        String destDir = "C:/test/ht/dd/";
        String destName = "LogNew.rar";
        System.out.printf("srcDir:%s, srcName:%s, destDir:%s, destName:%s",srcDir,srcName,destDir,destName);
        try {
            String srcDirNew = FileUtils.formatDir(srcDir);
            String destDirNew = FileUtils.formatDir(destDir);
            FileUtils.createDir(destDirNew);
            FileUtils.save(srcDirNew, srcName, destDirNew, destName);
        } catch (IOException e) {
            System.out.println("ERROR:"+e.getMessage());
            e.printStackTrace();
        }
    }

    private void testCoppy(){
        File inFile = new File("c:/test/read.txt");
        File outFile = new File("c:/test/read1.txt");
        try (FileInputStream fis = new FileInputStream(inFile);
             FileOutputStream fos = new FileOutputStream(outFile))
        {
            String fileName =formatFilename("~ !@#$%^&*()_+");
            System.out.println("fileName: " + fileName);
            long fileLength = inFile.length();
            byte[] inFileByte = new byte[(int) fileLength];
            System.out.println("fileLength:"+fileLength +" inFileByte:"+inFileByte.length);
            int i ;
            byte[] buff = new byte[fis.available()];
            while ((i= fis.read(buff)) >0){
                fos.write(buff,0,i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
